package com.momo.elasticsearch;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.momo.elasticsearch.entity.EcDataLogDO;
import com.momo.elasticsearch.entity.EcMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.SortField;
import org.elasticsearch.action.search.*;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;

/**
 * Created by MOMO on 2019/3/14.
 * https://www.elastic.co/guide/en/elasticsearch/reference/6.6/indices-rollover-index.html
 * https://www.elastic.co/guide/index.html
 */
@Service
@Slf4j
public class ActionSearchService {
    @Autowired
    private RestHighLevelClient client;
    @Autowired
    private EcMapper ecMapper;

    /**
     * 搜索 按精准条件搜索 和mysql的 = 一样
     * 布尔查询由一个或多个布尔子句组成，主要包含如下4个：
     * must：    #条件必须满足，相当于and，对于一个文档，所有的查询都必须为真，这个文档才能够匹配成功
     * should:   #相当于or，对于一个文档，查询列表中，只要有一个查询匹配，那么这个文档就被看成是匹配的
     * must_not: #相当于not，对于一个文档，查询列表中的的所有查询都必须都不为真，这个文档才被认为是匹配的
     * filter：  #只过滤符合条件的文档，不计算相关性得分
     * <p>
     * filter:es针对filter会有智能缓存，因此其执行效率很高，做简单匹配查询且不考虑算分时，推荐使用filter代替query
     * <p>
     * should:使用分两种情况：
     * <p>
     * bool查询中包含should,不包含must查询（文档必须满足至少一个条件，minimum_should_match可以控制满足条件的个数或百分比）
     * bool查询中同时包含should和must查询（文档不必满足should中的条件，但是如果满足条件，会增加相关性得分）
     *
     * @param index
     * @param type
     * @throws IOException
     */
    public PageInfo pageInfo(String index, String type) {
        PageInfo info = new PageInfo();
        try {
            //1 大多数搜索参数都添加到SearchSourceBuilder中。它为进入搜索请求体的所有内容提供setter。
            SearchRequest searchRequest = new SearchRequest("sys_core_alias");
            //2
            BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
            //3
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            List<String> list = ecMapper.getDeviceNos();

            //范围查询 mysql   beginTime<=collectTime and collectTime<=endTime
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("collectTime");
            rangeQueryBuilder.gt(1548602978000L);//开始时间
            rangeQueryBuilder.lt(2548602578000L);//结束时间
            //termsQuery  ==> mysql  IN   输入的查询内容是什么，就会按照什么去查询，并不会解析查询内容，对它分词
            //推荐使用
            boolQueryBuilder.must(QueryBuilders.termsQuery("deviceNo", list));
            //boolQueryBuilder.must(rangeQueryBuilder);
            // termQuery  ==> mysql  =  输入的查询内容是什么，就会按照什么去查询，并不会解析查询内容，对它分词
            //推荐使用
//            boolQueryBuilder.must(QueryBuilders.termQuery("deviceNo","0869858030025127" ));
            // termQuery  ==> mysql  =  搜索的时候，首先会解析查询字符串，进行分词，然后查询
//            boolQueryBuilder.must(QueryBuilders.matchQuery("deviceNo","0869858030025127" ));


            sourceBuilder.query(boolQueryBuilder);
            //分页使用
            sourceBuilder.from(9000);
            sourceBuilder.size(100); // 获取记录数，默认10
            // 第一个是获取字段，第二个是过滤的字段，默认获取全部
            sourceBuilder.fetchSource(new String[]{"*"}, new String[]{});
            //排序
            sourceBuilder.sort(new FieldSortBuilder("createTime").order(SortOrder.DESC));
            searchRequest.types(type);
            searchRequest.source(sourceBuilder);

            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = response.getHits();
            SearchHit[] searchHits = hits.getHits();
            log.info("查询文档总数:{},耗时：{},IN的个数:{}", hits.getTotalHits(), response.getTook(), list.size());
            List<EcDataLogDO> ecDataLogDOS = Lists.newArrayList();
            for (SearchHit hit : searchHits) {
                EcDataLogDO ecDataLogDO = JSON.parseObject(hit.getSourceAsString(), new TypeReference<EcDataLogDO>() {
                });
                ecDataLogDOS.add(ecDataLogDO);
            }
            info.setTotal(hits.getTotalHits());
            info.setList(ecDataLogDOS);
        } catch (IOException e) {
            log.error("搜索信息 异常:{}", e.getMessage());
        }
        return info;
    }

    public PageInfo searchScroll(String index, String type, String scrollIdTwo) {
        PageInfo info = new PageInfo();
        try {
            SearchRequest searchRequest = new SearchRequest(index);
            BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            List<String> list = ecMapper.getDeviceNos();

            //范围查询 mysql   beginTime<=collectTime and collectTime<=endTime
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("collectTime");
            rangeQueryBuilder.gt(1548602978000L);//开始时间
            rangeQueryBuilder.lt(2548602578000L);//结束时间
//            //termsQuery  ==> mysql  IN   输入的查询内容是什么，就会按照什么去查询，并不会解析查询内容，对它分词
//            //推荐使用
            boolQueryBuilder.must(QueryBuilders.termsQuery("deviceNo", list));
            boolQueryBuilder.must(rangeQueryBuilder);
            searchSourceBuilder.query(boolQueryBuilder);
            searchSourceBuilder.size(10);
            searchRequest.source(searchSourceBuilder);

            final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L));
            searchRequest.scroll(scroll);
            searchRequest.source(searchSourceBuilder);

            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            String scrollId = searchResponse.getScrollId();
            log.info("scrollId=== {}", scrollId);
            SearchHits hits = searchResponse.getHits();
            SearchHit[] searchHits = searchResponse.getHits().getHits();
            log.info("查询文档总数:{},耗时：{},IN的个数:{}", hits.getTotalHits(), searchResponse.getTook(), list.size());
            List<EcDataLogDO> ecDataLogDOS = Lists.newArrayList();
            for (SearchHit hit : searchHits) {
                EcDataLogDO ecDataLogDO = JSON.parseObject(hit.getSourceAsString(), new TypeReference<EcDataLogDO>() {
                });
                ecDataLogDOS.add(ecDataLogDO);
            }
            while (searchHits != null && searchHits.length > 0) {

                SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
                scrollRequest.scroll(scroll);
                searchResponse = client.scroll(scrollRequest, RequestOptions.DEFAULT);
                scrollId = searchResponse.getScrollId();
                searchHits = searchResponse.getHits().getHits();
                for (SearchHit hit : searchHits) {
                    log.info(hit.toString());
                }
            }
            info.setTotal(hits.getTotalHits());
            info.setList(ecDataLogDOS);
            //完成滚动后清除滚动上下文
           /* ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
            clearScrollRequest.addScrollId(scrollId);

            ClearScrollResponse clearScrollResponse = client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
            boolean succeeded = clearScrollResponse.isSucceeded();*/

        } catch (IOException e) {
            log.error("滚动分页 异常:{}", e.getMessage());
        }
        return info;
    }

    public PageInfo search_after(String index, String type) {
        PageInfo info = new PageInfo();
        try {
            SearchRequest searchRequest = new SearchRequest(index);
            BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            List<String> list = ecMapper.getDeviceNos();

            //范围查询 mysql   beginTime<=collectTime and collectTime<=endTime
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("collectTime");
            rangeQueryBuilder.gt(1548602978000L);//开始时间
            rangeQueryBuilder.lt(2548602578000L);//结束时间
            //termsQuery  ==> mysql  IN   输入的查询内容是什么，就会按照什么去查询，并不会解析查询内容，对它分词
            //推荐使用
            boolQueryBuilder.must(QueryBuilders.termsQuery("deviceNo", list));
            Object[] aa={303286163446304768L};
            sourceBuilder.searchAfter(aa);
            sourceBuilder.query(boolQueryBuilder);
            //分页使用
//            sourceBuilder.from(0);
            sourceBuilder.size(10); // 获取记录数，默认10
            // 第一个是获取字段，第二个是过滤的字段，默认获取全部
            sourceBuilder.fetchSource(new String[]{"*"}, new String[]{});
            //排序
            sourceBuilder.sort(new FieldSortBuilder("longid").order(SortOrder.DESC));
            searchRequest.types(type);
            searchRequest.source(sourceBuilder);
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = response.getHits();

            SearchHit[] searchHits = hits.getHits();
            log.info("查询文档总数:{},耗时：{},IN的个数:{}", hits.getTotalHits(), response.getTook(), list.size());
            List<EcDataLogDO> ecDataLogDOS = Lists.newArrayList();
            for (SearchHit hit : searchHits) {
                Object[] objects=   hit.getSortValues();
                log.info("sortFields[0]"+hit.getSortValues()[0]);
                EcDataLogDO ecDataLogDO = JSON.parseObject(hit.getSourceAsString(), new TypeReference<EcDataLogDO>() {
                });
                ecDataLogDOS.add(ecDataLogDO);
            }
            info.setTotal(hits.getTotalHits());
            info.setList(ecDataLogDOS);
        } catch (IOException e) {
            log.error("搜索信息 异常:{}", e.getMessage());
        }
        return info;
    }

}
